package com.ringtheweb.model;

import static javax.persistence.GenerationType.*;

import java.io.Serializable;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.Index;
import org.hibernate.validator.Email;
import org.hibernate.validator.Length;
import org.hibernate.validator.Pattern;

import com.ringtheweb.common.util.SecurityUtils;

@Entity
@Table(name="USERS")
@NamedQueries({
	@NamedQuery(name="findByLogin",query="select u from User as u where u.loginName=:loginName"),
	@NamedQuery(name="findByLoginAndPassword",query="select u from User as u where u.loginName=:loginName and u.password=:password")
	
})
public class User implements Serializable {
	
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = AUTO)
	private Long id;
	
	@Length(min=5, max=15)
	@Pattern(regex="^\\w*$", message="not a valid username")
	@Column(name="LOGIN",unique=true,precision=255)
	@Index(name="LOGIN_IDX")
	private String loginName;
	
	@Column(name="FIRST_NAME")
	private String firstName;
	
	@Column(name="LAST_NAME")
	private String lastName;
	
	@Length(min=5)
	@Column(name="PASSWORD")
	private String password;

	
	@ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "USER_ROLE",
        joinColumns = @JoinColumn(name = "USER_ID"),
        inverseJoinColumns = @JoinColumn(name = "ROLE_ID")
    )
    @OrderBy("displayName asc")
	private Set<Role> roles = new HashSet<Role>();

	
	@OneToMany
	@JoinColumn(name="USER_ID")
	@Fetch(FetchMode.JOIN)
	@org.hibernate.annotations.Cascade(
			{org.hibernate.annotations.CascadeType.DELETE_ORPHAN,
				org.hibernate.annotations.CascadeType.ALL})
	private Set<PhoneNumber> phoneNumbers = new HashSet<PhoneNumber>();
	
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name="ACTIVATION_DATE")
	private Date activationDate;
	
	@Column(name="EXPIRATION_DATE")
	@Temporal(TemporalType.TIMESTAMP)
	private Date expirationDate;
	
	@Email
	@Column(name="EMAIL")
	private String email;
	
	@Embedded
	private UserStatus status = new UserStatus(UserStatusType.TO_BE_APPROVED);
	
	public User() {}
	

	public User(String loginName) {
		this.loginName = loginName;
	}
	
	
	public Long getId() {
		return id;
	}


	public void setId(Long id) {
		this.id = id;
	}


	public String getFirstName() {
		return firstName;
	}


	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}


	public String getLastName() {
		return lastName;
	}


	public void setLastName(String lastName) {
		this.lastName = lastName;
	}


	public String getLoginName() {
		return loginName;
	}


	public void setLoginName(String loginName) {
		this.loginName = loginName;
	}

	public Set<PhoneNumber> getPhoneNumbers() {
		return phoneNumbers;
	}


	public void setPhoneNumbers(Set<PhoneNumber> phoneNumbers) {
		this.phoneNumbers = phoneNumbers;
	}


	public String getPassword() {
		return password;
	}


	public void setPassword(String password) {

		try {
			this.password=SecurityUtils.hashPassword(password.getBytes());
		}
		catch(NoSuchAlgorithmException e) {
			//log.error("internal failure when authenticating #0", this.getLoginName());
		}
	}

	public Set<Role> getRoles() {
		return roles;
	}

	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}
	
	public Date getActivationDate() {
		return activationDate;
	}


	public void setActivationDate(Date activationDate) {
		this.activationDate = activationDate;
	}


	public Date getExpirationDate() {
		return expirationDate;
	}


	public void setExpirationDate(Date expirationDate) {
		this.expirationDate = expirationDate;
	}
	
	
	public String getEmail() {
		return email;
	}


	public void setEmail(String email) {
		this.email = email;
	}


	public boolean isActive() {
		if (null==activationDate) return false;
		if (null == expirationDate ) return true;
		return expirationDate.after(activationDate);
	}


	public UserStatus getStatus() {
		return status;
	}


	public void setStatus(UserStatus status) {
		this.status = status;
		status.setUser(this);
	}


	@Override
    public String toString() {
        return  "User {'id=" + getId() + "',login=" +
                 getLoginName() + ",First Name='+" +
                 getFirstName() + ",Last Name='+" +
                 getLastName() +"}";
    }
    

    @Override
	public boolean equals(Object obj) {
		if (obj == this) {
			return true;
		}
		if (!(obj instanceof User)) {
			return false;
		}

		User user=(User)obj;
		return (this.loginName == null? user.getLoginName() == null :
			this.loginName.equals(user.getLoginName()));
	}

	@Override
	public int hashCode() {
		int result=17;
		return result + 31* (loginName == null ? 
				0 : loginName.hashCode());
	}


}
